/*
 *    Copyright 2009-2022 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       https://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.reflection;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;

import org.apache.ibatis.reflection.invoker.GetFieldInvoker;
import org.apache.ibatis.reflection.invoker.Invoker;
import org.apache.ibatis.reflection.invoker.MethodInvoker;
import org.apache.ibatis.reflection.property.PropertyTokenizer;

/**
 * 元类型
 * 通过 Reflector 和 PropertyTokenizer 组合使用，实现对复杂属性表达式的解析
 * 并实现了获取指定属性描述信息的功能
 *
 * @author Clinton Begin
 */
public class MetaClass {

  /**
   * 创建并可能缓存 Reflector
   */
  private final ReflectorFactory reflectorFactory;

  /**
   * 在创建 MetaClass 时会制定一个类，该 Reflector 类用于记录该类相关元信息
   */
  private final Reflector reflector;

  /**
   * 指派反射工厂并为指定类型 type 创建反射器 Reflector
   *
   * @param type
   * @param reflectorFactory
   */
  private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
    // 1. 指派一个 反射器工厂
    this.reflectorFactory = reflectorFactory;
    // 2. 通过反射工厂为指定类类型构造反射器
    this.reflector = reflectorFactory.findForClass(type);
  }

  /**
   * MetaClass 的静态生成方法
   * 该方法实例化一个 MetaClass 对象
   *
   * @param type
   * @param reflectorFactory
   * @return
   */
  public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
    return new MetaClass(type, reflectorFactory);
  }

  /**
   * 取指定属性getter类型的反射器
   *
   * @param name
   * @return
   */
  public MetaClass metaClassForProperty(String name) {
    Class<?> propType = reflector.getGetterType(name);
    return MetaClass.forClass(propType, reflectorFactory);
  }

  /**
   * 查询属性
   *
   * @param name
   * @return
   */
  public String findProperty(String name) {
    // 1. 委托给 buildProperty
    StringBuilder prop = buildProperty(name, new StringBuilder());
    return prop.length() > 0 ? prop.toString() : null;
  }

  public String findProperty(String name, boolean useCamelCaseMapping) {
    if (useCamelCaseMapping) {
      name = name.replace("_", "");
    }
    return findProperty(name);
  }

  public String[] getGetterNames() {
    return reflector.getGetablePropertyNames();
  }

  public String[] getSetterNames() {
    return reflector.getSetablePropertyNames();
  }

  public Class<?> getSetterType(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      MetaClass metaProp = metaClassForProperty(prop.getName());
      return metaProp.getSetterType(prop.getChildren());
    } else {
      return reflector.getSetterType(prop.getName());
    }
  }

  public Class<?> getGetterType(String name) {
    // 1. 解析属性表达式
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      // 2. 调用 metaClassForProperty() 方法
      MetaClass metaProp = metaClassForProperty(prop);
      // 3. 递归调用
      return metaProp.getGetterType(prop.getChildren());
    }
    // issue #506. Resolve the type inside a Collection Object
    // 4. 解析集合对象中的类型
    return getGetterType(prop);
  }

  private MetaClass metaClassForProperty(PropertyTokenizer prop) {
    Class<?> propType = getGetterType(prop);
    return MetaClass.forClass(propType, reflectorFactory);
  }

  private Class<?> getGetterType(PropertyTokenizer prop) {
    // 1. 取属性类型
    Class<?> type = reflector.getGetterType(prop.getName());
    // 2. 判断表达式中是否使用 [] 下标且是 Collection 类型
    if (prop.getIndex() != null && Collection.class.isAssignableFrom(type)) {
      // 3. 通过 TypeParameterResolver 工具类解析属性类型
      Type returnType = getGenericGetterType(prop.getName());
      // 4. 针对 ParameterizedType 进行处理，即针对泛型集合进行处理
      if (returnType instanceof ParameterizedType) {
        // 5. 获取实际类型参数
        Type[] actualTypeArguments = ((ParameterizedType) returnType).getActualTypeArguments();
        if (actualTypeArguments != null && actualTypeArguments.length == 1) {
          // 6. 泛型类型
          returnType = actualTypeArguments[0];
          if (returnType instanceof Class) {
            type = (Class<?>) returnType;
          } else if (returnType instanceof ParameterizedType) {
            type = (Class<?>) ((ParameterizedType) returnType).getRawType();
          }
        }
      }
    }
    return type;
  }

  private Type getGenericGetterType(String propertyName) {
    try {
      // 1. 根据 Reflector getMethods 集合中记录的 Invoker 实现类的类型，决定解析 getter 方法返回值类型还是解析字段类型
      Invoker invoker = reflector.getGetInvoker(propertyName);
      if (invoker instanceof MethodInvoker) {
        Field declaredMethod = MethodInvoker.class.getDeclaredField("method");
        declaredMethod.setAccessible(true);
        Method method = (Method) declaredMethod.get(invoker);
        return TypeParameterResolver.resolveReturnType(method, reflector.getType());
      } else if (invoker instanceof GetFieldInvoker) {
        Field declaredField = GetFieldInvoker.class.getDeclaredField("field");
        declaredField.setAccessible(true);
        Field field = (Field) declaredField.get(invoker);
        return TypeParameterResolver.resolveFieldType(field, reflector.getType());
      }
    } catch (NoSuchFieldException | IllegalAccessException e) {
      // Ignored
    }
    return null;
  }

  public boolean hasSetter(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      if (reflector.hasSetter(prop.getName())) {
        MetaClass metaProp = metaClassForProperty(prop.getName());
        return metaProp.hasSetter(prop.getChildren());
      } else {
        return false;
      }
    } else {
      return reflector.hasSetter(prop.getName());
    }
  }

  public boolean hasGetter(String name) {
    // 1. 解析属性表达式
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      // 2. 存在子表达式则判断字书写是否存在，若存在则解析
      if (reflector.hasGetter(prop.getName())) {
        MetaClass metaProp = metaClassForProperty(prop);
        return metaProp.hasGetter(prop.getChildren());
      } else {
        return false;
      }
    } else {
      return reflector.hasGetter(prop.getName());
    }
  }

  public Invoker getGetInvoker(String name) {
    return reflector.getGetInvoker(name);
  }

  public Invoker getSetInvoker(String name) {
    return reflector.getSetInvoker(name);
  }

  /**
   * 这里以解析 User 中的 tele.num 这个属性表达式为例:
   * 1、首先使用 PropertyTokenizer 解析 tele.num 表达式得到 children 宇段为 num, name 字段为 tel ；
   * 2、然后将 tele 追加到 builder 中保存，并调用 metaClassForProperty() 方法为 Tele 类创建对应的 MetaClass 对象，
   * 调用其 buildProperty（） 方法处理子表达式 num 逻辑同上，此时己经没有待处理的子表达式，最终得到 builder 中记录的字符串为 tele.num 了
   * (这里不关注下标)
   *
   */
  private StringBuilder buildProperty(String name, StringBuilder builder) {
    // 1. 新建分词器并对属性进行拆分
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      // 2. 存在子表达是则通过反射器取 PropertyTokenizer.name 属性名称
      String propertyName = reflector.findPropertyName(prop.getName());
      if (propertyName != null) {
        // 3. 追加属性名
        builder.append(propertyName);
        builder.append(".");
        // 4. 取该属性的元类型
        MetaClass metaProp = metaClassForProperty(propertyName);
        // 5. 递归取 PropertyTokenizer.children 对应属性并添加到 builder
        metaProp.buildProperty(prop.getChildren(), builder);
      }
    } else {
      String propertyName = reflector.findPropertyName(name);
      if (propertyName != null) {
        builder.append(propertyName);
      }
    }
    return builder;
  }

  public boolean hasDefaultConstructor() {
    return reflector.hasDefaultConstructor();
  }

}
